/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace DaveSexton.DocProject
{
	public class SortedPropertyConverter : TypeConverter
	{
		#region Public Properties
		public StringComparison SameIndexPropertyComparison
		{
			get
			{
				return comparison;
			}
			set
			{
				comparison = value;
			}
		}
		#endregion

		#region Private / Protected
		private StringComparison comparison = StringComparison.CurrentCultureIgnoreCase;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="SortedPropertyConverter" /> class.
		/// </summary>
		public SortedPropertyConverter()
		{
		}
		#endregion

		#region Methods
		public sealed override bool GetPropertiesSupported(ITypeDescriptorContext context)
		{
			return true;
		}

		public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
		{
			return GetSortedProperties(context, TypeDescriptor.GetProperties(value, attributes));
		}

		protected virtual PropertyDescriptorCollection GetSortedProperties(ITypeDescriptorContext context, PropertyDescriptorCollection unsortedProperties)
		{
			return unsortedProperties.Sort(new IndexNameComparer(this));
		}

		protected virtual int GetPropertySortIndex(MemberDescriptor member)
		{
			if (member == null)
				throw new ArgumentNullException("member");

			SortedPropertyAttribute attribute = member.Attributes[typeof(SortedPropertyAttribute)] as SortedPropertyAttribute;

			return (attribute == null) ? 0 : attribute.SortIndex;
		}

		public virtual PropertyDescriptorCollection Sort(PropertyDescriptorCollection properties)
		{
			if (properties == null)
				throw new ArgumentNullException("properties");

			return properties.Sort(new IndexNameComparer(this));
		}
		#endregion

		#region Nested
		private sealed class IndexNameComparer : Comparer<PropertyDescriptor>
		{
			private readonly SortedPropertyConverter owner;

			public IndexNameComparer(SortedPropertyConverter owner)
			{
				if (owner == null)
					throw new ArgumentNullException("owner");

				this.owner = owner;
			}

			public override int Compare(PropertyDescriptor x, PropertyDescriptor y)
			{
				if (x == null)
					return (y == null) ? 0 : -1;
				else if (y == null)
					return 1;

				int result = owner.GetPropertySortIndex(x).CompareTo(
					owner.GetPropertySortIndex(y));

				if (result == 0)
					return string.Compare(x.Name, y.Name, owner.SameIndexPropertyComparison);
				else
					return result;
			}
		}
		#endregion
	}
}
